home *** CD-ROM | disk | FTP | other *** search
/ QuickTime 2.0 Developer Kit / QuickTime 2.0 Developer Kit.iso / mac / MAC / Programming Stuff / Sample Code / Movie Data Exchange / JPEG File Interchange Format / JFIF Preview Component / JFIFPreviewer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-10-22  |  15.3 KB  |  664 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        JFIFPreviewer.c
  3. */
  4.  
  5.  
  6. #include <Aliases.h>
  7. #include <Files.h>
  8. #include <Errors.h>
  9. #include <ToolUtils.h>
  10. #include <Memory.h>
  11. #include <QDOffscreen.h>
  12. #include <Components.h>
  13. #include <ImageCompression.h>
  14.  
  15.  
  16.  
  17.  
  18. pascal ComponentResult PreviewShowData(pnotComponent p, OSType dataType, Handle data,
  19.         const Rect *inHere) = ComponentCallNow(1, 12);
  20.  
  21.  
  22. /*
  23.  
  24.     NOTE: to install this previewer ( or any other components ) inside your application:
  25.     
  26.     put all the resources into your app then do this:
  27.     
  28.     {
  29.         Handle h;
  30.         short i,c;
  31.         short myAppFile;            // the res file refnum of your app = current res file at launch
  32.         Boolean everyBody = false;    // set to true to let other apps use component 
  33.         
  34.         UseResFile(myAppFile);
  35.         c = Count1Resources('thng');
  36.         for ( i=1; i <= c; i++ )
  37.             h = Get1IndResource('thng',i);
  38.             RegisterComponentResource((ComponentResourceHandle)h,everyBody);
  39.             ReleaseResource(h);
  40.         }
  41.     }
  42.     
  43. */
  44.  
  45.  
  46. #define    DEFAULT_COMPRESSION        'rpza'                // compressor for created previews - zero for uncompressed
  47. #define    DEFAULT_QUALITY            codecHighQuality    // quality level for created previews
  48.  
  49. #define    SBSIZE                    2048            // size of scan buffer for scanning for JFIF header
  50.  
  51.  
  52. /***********
  53.  
  54.     struct for deep progressProc.
  55.     
  56. ***********/
  57.  
  58. typedef     struct {
  59.     ProgressProcRecordPtr    progress;
  60.     GDHandle    gd;
  61.     CGrafPtr    port;
  62.     Fixed    start,end;
  63. } adpRec;
  64.  
  65. /***********
  66.  
  67.     struct for spooling with dataProcs.
  68.     
  69. ***********/
  70.  
  71. typedef    struct {
  72.     Ptr        bufEnd;
  73.     Ptr        bufStart;
  74.     long    bufSize;
  75.     long    size;
  76.     short    refNum;
  77. } DPRec;
  78.  
  79.  
  80. /***********
  81.     
  82.     function prototypes
  83.     
  84. **********/
  85.  
  86. pascal ComponentResult JFIFPreviewDispatch( ComponentParameters *params, Handle storage );
  87. pascal ComponentResult showJFIFPreview(OSType dataType, PicHandle data, const Rect *inHere);
  88. pascal ComponentResult makeJFIFPreview(OSType *previewType, Handle *previewResult,
  89.                     const FSSpec *sourceFile, ProgressProcRecordPtr progress);
  90. pascal ComponentResult    CanDoSelector(short selector);
  91. pascal OSErr    adpProc(short msg,Fixed pct,long refcon);
  92. pascal OSErr    dataProc(Ptr *cp,long sizeNeeded,long refcon);
  93. OSErr    ReadJFIFThumbnail(short refNum, Handle *thumbnail, ProgressProcRecordPtr progress, Boolean thumbOnly);
  94.  
  95.  
  96. /***************************************************
  97.  
  98.     Component entry point.
  99.  
  100. ***************************************************/
  101.  
  102. pascal ComponentResult JFIFPreviewDispatch( ComponentParameters *params, Handle storage )
  103. {
  104. #pragma unused(storage)
  105.     ComponentFunction    componentProc = 0;
  106.     ComponentResult err = 0;
  107.  
  108.     switch (params->what) {
  109.         case kComponentOpenSelect:
  110. #ifdef THINK_C
  111.             SetComponentInstanceA5((ComponentInstance)params->params[0], *(long *)CurrentA5);
  112. #endif    
  113.             break;
  114.         case kComponentCloseSelect:
  115.             break;
  116.         case kComponentCanDoSelect:
  117.             return CallComponentFunction(params,(ComponentFunction)CanDoSelector);    
  118.             break;
  119.         case kComponentVersionSelect:
  120.             return 0;
  121.             break;
  122.         case 1:    
  123.             componentProc = (ComponentFunction)showJFIFPreview;
  124.             break;
  125.         case 2: 
  126.             componentProc = (ComponentFunction)makeJFIFPreview;
  127.             break;
  128.     }
  129.  
  130.     if (componentProc)
  131.         err = CallComponentFunction(params, componentProc);
  132.  
  133.     return err;
  134. }
  135.  
  136. /***************************************************
  137.  
  138.     Indicate whether we can handle the call or not.
  139.     
  140. ***************************************************/
  141.  
  142. pascal ComponentResult
  143. CanDoSelector(short selector)
  144. {    
  145.     switch (selector) {
  146.     case kComponentOpenSelect:
  147.     case kComponentCloseSelect:
  148.     case kComponentCanDoSelect:
  149.     case kComponentVersionSelect: 
  150.     case 1:
  151.     case 2:
  152.         return(true);
  153.     default:
  154.         return (false);
  155.     }
  156. }
  157.  
  158. /***************************************************
  159.  
  160.     We are asked to make a thumbnail from a JFIF file. We comply.
  161.  
  162. ***************************************************/
  163.     
  164.  
  165. pascal ComponentResult makeJFIFPreview(OSType *previewType, Handle *previewResult,
  166.                     const FSSpec *sourceFile, ProgressProcRecordPtr progress)
  167. {
  168.     OSErr err;
  169.     Handle thumbnail = 0;
  170.     short refNum;
  171.  
  172.     err = FSpOpenDF(sourceFile, fsRdPerm, &refNum);
  173.     *previewResult = nil;
  174.     if (!err) {
  175.         err = ReadJFIFThumbnail(refNum, previewResult, progress, false);
  176.         FSClose(refNum);
  177.     }
  178.  
  179. bail:
  180.     if (!err) {
  181.         *previewType = 'PICT';
  182.     } else if ( *previewResult ) {
  183.         DisposHandle(*previewResult);
  184.         *previewResult = nil;
  185.     }
  186.     return(err);
  187. }
  188.  
  189.  
  190. /***************************************************
  191.  
  192.     Called to show the preview. Data is a picture, type is 'PICT';
  193.     
  194. ***************************************************/
  195.  
  196. pascal ComponentResult showJFIFPreview(OSType dataType, PicHandle data, const Rect *inHere)
  197. {
  198.     OSErr err = noErr;
  199.     Handle thumbnail = 0;
  200.     short refNum;
  201.     FSSpec theFile;
  202.     Boolean whoCares;
  203.     Handle thePict = nil;
  204.     ComponentInstance ci;
  205.  
  206.     if (dataType != rAliasType)
  207.         return paramErr;
  208.  
  209.     if (err = ResolveAlias(nil, (AliasHandle)data, &theFile, &whoCares)) goto bail;
  210.  
  211.     err = FSpOpenDF(&theFile, fsRdPerm, &refNum);
  212.     if (!err) {
  213.         err = ReadJFIFThumbnail(refNum, &thePict, nil, true);
  214.         FSClose(refNum);
  215.     }
  216.     if (err) goto bail;
  217.  
  218.     if (ci = OpenDefaultComponent('pnot', 'PICT')) {
  219.         PreviewShowData(ci, 'PICT', thePict, inHere);
  220.         CloseComponent(ci);
  221.     }
  222.  
  223.     KillPicture((PicHandle)thePict);
  224.  
  225. bail:
  226.     return err;
  227. }
  228.  
  229.  
  230.  
  231. /***************************************************
  232.  
  233.     This does the work. It checks for a JFIF thumbnail and if there is one,
  234.     it makes a PICT out of it. If not it draws the JFIF file into a 96x96 (max)
  235.     offscreen and makes a PICT out of that.
  236.  
  237. ***************************************************/
  238.  
  239. OSErr
  240. ReadJFIFThumbnail(short refNum, Handle *thumbnail, ProgressProcRecordPtr progress, Boolean thumbOnly)
  241.  
  242. {
  243.     long    refcon = 0;
  244.  
  245.     unsigned char buf[SBSIZE];
  246.     long l = SBSIZE;
  247.     short i;
  248.     short    j,w = 0,h = 0;
  249.     Rect    frame;
  250.     PicHandle pict = nil;
  251.     GWorldPtr    gw = nil;
  252.     CGrafPtr savePort;
  253.     GDHandle    saveGD;
  254.     unsigned char *bp,*rp;
  255.     short    rb;
  256.     char    mmuMode = 1;
  257.     char    oldMMUMode;
  258.     short x,y;
  259.     Ptr        tp;
  260.     short     err = 0;
  261.     short    numCom = 0;
  262.     short    hv1 =0,hv2=0,hv3=0;
  263.     
  264.     /* call the progress proc if we have to. */
  265.     
  266.     if ( progress)  {
  267.         if ( progress->progressProc(codecProgressOpen,0,progress->progressRefCon) ) {
  268.             err = -1;
  269.             goto done;
  270.         }
  271.         if ( progress->progressProc(codecProgressUpdatePercent,0x1,progress->progressRefCon) ) {
  272.             err = -1;
  273.             goto done;
  274.         }
  275.     }
  276.     
  277.     GetGWorld(&savePort,&saveGD);
  278.     FSRead(refNum,&l,(Ptr)buf);
  279.     
  280.     /* scan the JFIF stream for a JFIF header, or a SOF header */
  281.     
  282.     for ( i=0; i < SBSIZE; i++ ) {
  283.         if ( buf[i] == 0xff ) {
  284.             i++;
  285.             
  286.             /* JFIF header */
  287.             
  288.             if ( buf[i] == (unsigned char)0xe0 ) {
  289.                 i++;
  290.                 j = (buf[i] << 8) | buf[i+1];
  291.                 i++;
  292.                 if ( j <= 16 )                    /* no thumbnail - keep scanning */
  293.                     continue;
  294.                 if ( buf[i+1] == 'J'  &&
  295.                     buf[i+2] == 'F'  &&
  296.                     buf[i+3] == 'I'  &&
  297.                     buf[i+4] == 'F' &&
  298.                     buf[i+5] == 0 &&
  299.                     buf[i+6] == 1 &&
  300.                     buf[i+7] <= 1 ) {
  301.                     w = buf[i+13];
  302.                     h = buf[i+14];
  303.                     if ( w == 0 || h == 0 )        /* no thumbnail - keep scanning */
  304.                         continue;
  305.                     else {
  306.                     
  307.                         /* read in the thumbnail - its 8-8-8 rgb for w*h pixels */
  308.                         
  309.                         l = w*h*3;
  310.                         SetFPos(refNum,fsFromStart,(long)i+15);
  311.                         i = 0;
  312.                         tp = NewPtr(l);
  313.                         if ( tp == nil ) {
  314.                             err = -108;
  315.                             goto done;
  316.                         }
  317.                         if ( progress)  {
  318.                             if ( progress->progressProc(codecProgressUpdatePercent,0x0100,progress->progressRefCon) ) {
  319.                                 err = -1;
  320.                                 goto done;
  321.                             }
  322.                         }
  323.                         FSRead(refNum,&l,tp);
  324.                         SetRect(&frame,0,0,w,h);
  325.                         
  326.                         /* make a 16-bit gworld and copy the thumbnail into it */
  327.                         
  328.                         if ( NewGWorld(&gw,16,&frame,nil,nil,0) == 0 || 
  329.                              NewGWorld(&gw,16,&frame,nil,nil,8) == 0 ) {
  330.                             LockPixels(gw->portPixMap);
  331.                             bp = (unsigned char *)GetPixBaseAddr(gw->portPixMap);
  332.                             rb = (*gw->portPixMap)->rowBytes & 0x3fff;
  333.                             oldMMUMode = GetMMUMode();
  334.                             SwapMMUMode(&mmuMode);
  335.                             for ( y=0; y < h; y++ ) {
  336.                                 rp = bp;
  337.                                 if ( progress) { 
  338.                                     SwapMMUMode(&oldMMUMode);
  339.                                     if ( progress->progressProc(codecProgressUpdatePercent,0x1000 + (0x9000  * y) / h,progress->progressRefCon) ) {
  340.                                         err = -1;
  341.                                         goto done;
  342.                                     }
  343.                                     SwapMMUMode(&oldMMUMode);
  344.                                 }
  345.                                 for ( x=0; x < w; x++ ) {
  346.                                     short pix = (0x1f & (tp[i++]>>3)) << 10;
  347.                                     pix |= (0x1f & (tp[i++]>>3)) << 5;
  348.                                     pix |= (0x1f & (tp[i++]>>3));
  349.                                     *(short *)rp = pix;
  350.                                     rp += 2;
  351.                                 }
  352.                                 bp += rb;
  353.                             }
  354.                             SwapMMUMode(&mmuMode);
  355.                             DisposPtr(tp);
  356.                             break;            /* okay we got it - get outof the scan loop */
  357.                         } else {
  358.                             err = -108;
  359.                             goto done;
  360.                         }
  361.                     }
  362.                 } else {
  363.                     err = -108;        // no memory
  364.                     goto done;
  365.                 }
  366.                 
  367.                 /* start of frame header - grab the width and height */
  368.                 
  369.             } else if ( buf[i] == (unsigned char)0xc0 ) {
  370.                 i += 4;
  371.                 h = (buf[i]<<8) | buf[i+1];
  372.                 i += 2;
  373.                 w = (buf[i]<<8) | buf[i+1];
  374.                 i += 2;
  375.                 numCom = buf[i];
  376.                 if ( numCom == 3 ) {
  377.                     i += 2;
  378.                     hv1 = buf[i];
  379.                     i += 3;
  380.                     hv2 = buf[i];
  381.                     i += 3;
  382.                     hv3 = buf[i];
  383.                 }    
  384.                 break;
  385.             }
  386.         }
  387.     }
  388.     /* couldn't find a SOF header - so bail */
  389.     
  390.     if ( w == 0 || h == 0 ) {
  391.         err = -50;
  392.         goto done;
  393.     }
  394.     
  395.     /* there was no thumbnail - so draw the whole image */
  396.     
  397.     if ( gw == nil ) {
  398.         ImageDescriptionHandle desc = nil;
  399.         DataProcRecord dataP,*dataPP = nil;
  400.         DPRec dataRec;
  401.         ProgressProcRecord pproc;
  402.         adpRec adpRec;
  403.         MatrixRecord matrix;
  404.         short    tw,th;
  405.         Rect    tframe;
  406.         Ptr    buffer = nil;
  407.         
  408.         if (thumbOnly) {
  409.             err = -50;
  410.             goto done;
  411.         }
  412.  
  413.         if ( (desc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription))) == nil ) {
  414.             err = MemError();
  415.             goto cbail;
  416.         }
  417.         /* make up an image description */
  418.         
  419.         (*desc)->idSize = sizeof(ImageDescription);
  420.         (*desc)->temporalQuality = 0;
  421.         (*desc)->spatialQuality = codecNormalQuality;
  422.         (*desc)->dataSize = l;
  423.         (*desc)->cType = 'jpeg';
  424.         (*desc)->version = 0;
  425.         (*desc)->revisionLevel = 0;
  426.         (*desc)->vendor = 0;
  427.         (*desc)->hRes = 72L<<16;
  428.         (*desc)->vRes = 72L<<16;
  429.         (*desc)->width = w;
  430.         (*desc)->height = h;
  431.         (*desc)->depth = 32;
  432.         (*desc)->clutID = -1;
  433.         BlockMove("\pPhoto - JPEG",(*desc)->name,13);
  434.         
  435.         if ( w > h ) {
  436.             tw = 96;
  437.             th = h * 96 / w ;
  438.         } else {
  439.             th = 96;
  440.             tw = w * 96 / h ;
  441.         }
  442.         SetRect(&tframe,0,0,tw,th);
  443.         SetRect(&frame,0,0,w,h);
  444.         
  445.         /* make a gworld up to 96x96 pixels and draw the JFIF file in it */
  446.         
  447.         if ( NewGWorld(&gw,16,&tframe,nil,nil,0) == 0 || 
  448.              NewGWorld(&gw,16,&tframe,nil,nil,8) == 0 ) {
  449.              
  450.              
  451.             if ( progress ) {
  452.             
  453.                 /* make a progressproc to call for partial progress */
  454.                 
  455.                 adpRec.progress = progress;
  456.                 adpRec.port = savePort;
  457.                 adpRec.gd = saveGD;
  458.                 adpRec.start = 0x1000;
  459.                 adpRec.end = 0xa000;
  460.                 
  461.                 pproc.progressProc = adpProc;
  462.                 pproc.progressRefCon = (long)&adpRec;
  463.                 if ( progress->progressProc(codecProgressUpdatePercent,0x0100,progress->progressRefCon) ) {
  464.                     err = -1;
  465.                     goto cbail;
  466.                 }
  467.             }
  468.             
  469.             /* read in file */
  470.             
  471.             GetEOF(refNum,&l);
  472.             if ( (buffer = NewPtr(l)) == nil ) {
  473.             
  474.             
  475.                 /* if we cant fit the whole thing in memory - make a dataProc to spool it in */
  476.                 
  477.                 if ( (buffer= NewPtr(codecMinimumDataSize)) == nil ) {
  478.                     err = MemError();
  479.                     goto cbail;
  480.                 }
  481.                 dataRec.refNum = refNum;
  482.                 dataRec.bufStart = buffer;
  483.                 dataRec.bufEnd = buffer + codecMinimumDataSize;
  484.                 dataRec.size = l - codecMinimumDataSize;
  485.                 dataRec.bufSize = codecMinimumDataSize;
  486.                 dataPP = &dataP;
  487.                 dataP.dataRefCon = (long)&dataRec;
  488.                 dataP.dataProc = dataProc;
  489.                 l = codecMinimumDataSize;
  490.             }
  491.             if ( progress ) {
  492.                 if ( progress->progressProc(codecProgressUpdatePercent,0x0800,progress->progressRefCon) ) {
  493.                     err = -1;
  494.                     goto cbail;
  495.                 }
  496.             }
  497.             SetFPos(refNum,fsFromStart,0);
  498.             if ( (err=FSRead(refNum,&l,buffer)) != 0 ) 
  499.                 goto cbail;
  500.             if ( progress ) {
  501.                 if ( progress->progressProc(codecProgressUpdatePercent,0x1000,progress->progressRefCon) ) {
  502.                     err = -1;
  503.                     goto cbail;
  504.                 }
  505.             }
  506.             RectMatrix(&matrix,&frame,&tframe);
  507.             SetGWorld(gw,nil);
  508.             err=FDecompressImage(buffer,desc,gw->portPixMap,
  509.                         &frame,&matrix,ditherCopy,(RgnHandle)nil,
  510.                         (PixMapHandle)nil,(Rect *)nil,codecHighQuality,anyCodec,0,
  511.                         dataPP,progress ?  &pproc : nil);
  512.             frame = tframe;
  513. cbail:
  514.             SetGWorld(savePort,saveGD);
  515.             if ( buffer )
  516.                 DisposPtr(buffer);
  517.             if ( desc )
  518.                 DisposHandle((Handle)desc);
  519.             if ( err )    
  520.                 goto done;
  521.         } else {
  522.             err = -108;
  523.             goto done;
  524.         }
  525.     }
  526.  
  527.     /* if we get here than gw holds the image for the thumbnail - put it in a PICT and compress it */
  528.  
  529.     if ( gw ) {
  530.         if ( progress)  {
  531.             if ( progress->progressProc(codecProgressUpdatePercent,0xa000,progress->progressRefCon) ) {
  532.                 err = -1;
  533.                 goto done;
  534.             }
  535.         }
  536.         SetGWorld(gw,nil);
  537.         pict = OpenPicture(&frame);
  538.         ClipRect(&frame);
  539.         EraseRect(&frame);
  540.         CopyBits((BitMap *)*gw->portPixMap,(BitMap *)*gw->portPixMap,
  541.             &gw->portRect,&gw->portRect,ditherCopy,nil);
  542.         ClosePicture();
  543.         SetGWorld(savePort,saveGD);
  544.         if (thumbOnly) {
  545.             *thumbnail = (Handle)pict;
  546.             goto done;
  547.         }
  548.  
  549.         if ( progress)  {
  550.             if ( progress->progressProc(codecProgressUpdatePercent,0xb000,progress->progressRefCon) ) {
  551.                 err = -1;
  552.                 goto done;
  553.             }
  554.         }
  555.         DisposeGWorld(gw);
  556.         gw  = nil;
  557.         if ( GetHandleSize((Handle)pict) <= sizeof(Picture) ) {
  558.             DisposHandle((Handle)pict);
  559.             err = -108;                // no memory
  560.         } else {
  561.             *thumbnail = NewHandle(sizeof(Picture));
  562.             if ( progress) {
  563.                 if ( progress->progressProc(codecProgressUpdatePercent,0xc000,progress->progressRefCon) ) {
  564.                     err = -1;
  565.                     goto done;
  566.                 }
  567.             }
  568.             if ( DEFAULT_COMPRESSION != 0 ) {
  569.                 if ( (err=CompressPicture(pict,(PicHandle)*thumbnail,DEFAULT_QUALITY,DEFAULT_COMPRESSION)) != 0 ) {
  570.                     DisposHandle(*thumbnail);
  571.                     *thumbnail = nil;
  572.                     DisposHandle((Handle)pict);
  573.                     goto done;
  574.                 }
  575.                 DisposHandle((Handle)pict);
  576.             } else {
  577.                 *thumbnail = (Handle)pict;
  578.             }
  579.             if ( progress) 
  580.                 progress->progressProc(codecProgressUpdatePercent,0x10000,progress->progressRefCon);
  581.         }
  582.     }
  583. done:
  584.     if ( gw )
  585.         DisposeGWorld(gw);
  586.     SetGWorld(savePort,saveGD);
  587.     if ( progress) 
  588.         progress->progressProc(codecProgressClose,0,progress->progressRefCon);
  589.     return(err);
  590. }
  591.  
  592.  
  593.  
  594. /***************************************************
  595.  
  596.     Deep progressproc - scales calls from proc and passes them up to higher level.
  597.     
  598. ***************************************************/
  599.  
  600. pascal OSErr
  601. adpProc(short msg,Fixed pct,long refcon) 
  602. {
  603.     OSErr    res = 0;
  604.     CGrafPtr savePort;
  605.     GDHandle    saveGD;
  606.     adpRec *adpr = (adpRec *)refcon;
  607.     
  608.     if ( msg == codecProgressUpdatePercent ) {
  609.         pct = adpr->start + FixMul(pct,adpr->end-adpr->start);
  610.         GetGWorld(&savePort,&saveGD);
  611.         SetGWorld(adpr->port,adpr->gd);
  612.         res = adpr->progress->progressProc(msg,pct,adpr->progress->progressRefCon);
  613.         SetGWorld(savePort,saveGD);
  614.     }
  615.     return(res);
  616. }
  617.  
  618. /***************************************************
  619.  
  620.     DataProc for spooling in compressed data.
  621.     
  622. ***************************************************/
  623.  
  624. pascal OSErr
  625. dataProc(Ptr *cp,long sizeNeeded,long refcon)
  626. {
  627.     char     mode = 0;
  628.     Ptr        current;
  629.     long    newChunk;
  630.     long    leftOver;
  631.     long    size;
  632.     OSErr     result = noErr;
  633.     DPRec     *dpr = (DPRec *)refcon;
  634.     
  635.  
  636.     if ( cp == nil ) 
  637.         return(-1);            /* dont do random access yet */
  638.         
  639.     current = *cp;
  640.     SwapMMUMode(&mode);
  641.     if ( (current + sizeNeeded) > dpr->bufEnd )  {
  642.     
  643.         // move whats left up to the front of the buffer
  644.         
  645.         leftOver = dpr->bufEnd - current;
  646.         BlockMove(current,dpr->bufStart,leftOver);        
  647.         newChunk = dpr->bufSize - leftOver;
  648.         if ( dpr->size < newChunk )
  649.             newChunk = dpr->size;
  650.         if ( newChunk ) {
  651.             size = newChunk;
  652.             FSRead(dpr->refNum,&size,dpr->bufStart+leftOver);
  653.             dpr->size -= newChunk;
  654.             dpr->bufEnd = dpr->bufStart+leftOver+newChunk;
  655.         }
  656.         *cp = dpr->bufStart;
  657.     }
  658.     SwapMMUMode(&mode);
  659.     return(result);
  660. }
  661.  
  662.  
  663.  
  664.